//***************************************************************************
//
//  File........: main.c
//
//  Author(s)...: ATMEL Norway
//
//  Target(s)...: ATmega169
//
//  Compiler....: AVR-GCC 4.3.2; avr-libc 1.6.6 (WinAVR 20090313)
//
//  Description.: AVR Butterfly main module
//
//  Revisions...: 79
//
//  Modified last time by Miguel Velazquez, Disha Kherde and Hector Silva on 26/06/2013
//
//  
//
//***************************************************************************

// (*) Martin Thomas, Kaiserslautern, Germany, e-mail: mthomas(at)rhrk.uni-kl.de 
// or eversmith(at)heizung-thomas.de
//
// I'm not working for ATMEL.
// The port is based on REV_06 of the ATMEL-Code (for IAR-C) with fixes/updates
// from REV_07.
// Initially I marked my changes with "// mt" or enclosed them with "// mtA" and 
// "// mtE" but forgot this for some changes esp. during debugging. 'diff' 
// against the original code to see everything that has been changed.

//mtA
//#include <inavr.h>
//#include "iom169.h"
#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
#include <avr/version.h>

#if __AVR_LIBC_VERSION__ < 10405UL
#warning "avr-libc >= version 1.4.5 recommended"
#warning "This code has not been tested with older versions."
#endif
//mtE

#include "main.h"
#include "LCD_functions.h"
#include "LCD_driver.h"
#include "button.h"
#include "RTC.h"
#include "BCD.h"
#include "usart.h"
#include "ADC.h"
#include "dataflash.h"
#include "data_functions.h"


#define pLCDREG_test (*(char *)(0xEC))

char PowerSaveTimeout = 30;     // Initial value, enable power save mode after 30 min
BOOL AutoPowerSave    = TRUE;   // Variable to enable/disable the Auto Power Save func

char PowerSave = FALSE;

extern volatile int16_t temperature;
extern char data[4];
extern volatile uint8_t  gSECOND;
extern uint32_t data_count;
extern uint32_t data_to_read;

char next_state=ST_MAIN;
char prev_state;
volatile uint8_t PauseFlag=0;


/*****************************************************************************
*
*   Function name : main
*
*   Returns :       None
*
*   Parameters :    None
*
*   Purpose :       Contains the main loop of the program
*
*****************************************************************************/


int main(void)
{    

    char input;
	char flag=0;//detects if a change in gSECOND

    char buttons;
    char last_buttons;

    last_buttons='\0';  // mt



    // Program initalization
    Initialization();
    sei(); // mt __enable_interrupt();
	LCD_puts("TEMPERATURE LOGGER", 1);
	while(gSECOND<5);

    for (;;)            // Main loop
    {


        input = getkey();           // Read buttons
		switch(next_state){
			case ST_MAIN: //Enter main state - Read and log data
				if(input==KEY_PLUS) next_state=ST_SET_INT;
				else if(input==KEY_MINUS) next_state=ST_STOP_GO;
				else if(input==KEY_NEXT) next_state=ST_READ_DATA;
				else if(input==KEY_PREV) next_state=ST_TRANSF_PC;
				else{
					
					display_value_LCD(temperature); // Update on LCD
				}
				prev_state=ST_MAIN;
				break;
			case ST_SET_INT: //Set time interval for logging
				if(PauseFlag==0) //Perform task only if not paused
				{
				SetInterval(input); //Selection of time interval
				}
				if(input==KEY_ENTER) next_state=ST_MAIN; //exit state to main state
				prev_state=ST_SET_INT;
				break;
			case ST_READ_DATA: //display stored values on LCD
				if(input==KEY_ENTER) next_state=ST_MAIN;
				else if(input==KEY_PREV){
					read_data(PREV_DATA); 

				}
				else if(input==KEY_NEXT){
					read_data(NEXT_DATA);
					
				}
				if((gSECOND%2)==0){
					ShowTimeStamp(data[3],data[2],data[1]);
				}
				else display_value_LCD(data[0]);

				prev_state=ST_READ_DATA;
				break;
			case ST_STOP_GO: // Pause measurement on user request
				if(PauseFlag==0){
				LCD_puts("Paused", 1);
				PauseFlag=1; //Set Pause Flag to pause measurements
				}
				if(input==KEY_ENTER)
				{ next_state=ST_MAIN;
				PauseFlag=0; //Reset Pause flag to resume normal tasks
				}
				prev_state=ST_STOP_GO;
				break;
			case ST_TRANSF_PC:  // Transfer data to PC
				if(prev_state==ST_MAIN) LCD_puts("PRESS ENTER TO TRANSFER DATA TO PC", 1);
				if(input==KEY_ENTER){
					LCD_puts("TRANSFERING DATA TO PC", 1); //indicate task on LCD
					data_to_read=data_count+4-data_count%4;
					do{
						read_data(PREV_DATA);
						if(data[0]==0&&data[1]==0&&data[2]==0&&data[3]==0){ 
							Usart_puts("No data stored\n");
							break;
						}
						else{
							Usart_Tx('\n');
							Usart_uint8(data[0]);
							Usart_Tx('\t');
							if(data[3]<10) Usart_Tx('0');
							Usart_uint8(data[3]);
							Usart_Tx(':');
							if(data[2]<10) Usart_Tx('0');
							Usart_uint8(data[2]);
							Usart_Tx(':');
							if(data[1]<10) Usart_Tx('0');
							Usart_uint8(data[1]);
						}

					}while(data_to_read>0);
					next_state=ST_MAIN;
				}
				
				prev_state=ST_TRANSF_PC;
				break;

		}

if(PauseFlag==0){ // if paused flag is set do not perform measurements
		if((gSECOND%2)==0 && flag==0){
			ADC_periphery(TEMPERATURE_SENSOR);
			flag=1;
		}
		if((gSECOND%2)==1) flag=0;
		check_for_interval();
}

        // Check if the joystick has been in the same position for some time, 
        // then activate auto press of the joystick
        buttons = (~PINB) & PINB_MASK;
        buttons |= (~PINE) & PINE_MASK;
        
        if( buttons != last_buttons ) 
        {
            last_buttons = buttons;
            gAutoPressJoystick = FALSE;
        }
        else if( buttons )
        {
            if( gAutoPressJoystick == TRUE)
            {
                PinChangeInterrupt();
                gAutoPressJoystick = AUTO;
            }
            else    
                gAutoPressJoystick = AUTO;
        }

        
    } //End Main loop

    return 0; // mt
}

/*****************************************************************************
*
*   Function name : Initialization
*
*   Returns :       None
*
*   Parameters :    None
*
*   Purpose :       Initializate the different modules
*
*****************************************************************************/
void Initialization(void) 
{
    char tst;           
	PauseFlag=0;  // Reset pause flag to 0, default state not paused

    OSCCAL_calibration();       // calibrate the OSCCAL byte
        
    CLKPR = (1<<CLKPCE);        // set Clock Prescaler Change Enable

    // set prescaler = 8, Inter RC 8Mhz / 8 = 1Mhz
    CLKPR = (1<<CLKPS1) | (1<<CLKPS0);

    // Disable Analog Comparator (power save)
    ACSR = (1<<ACD);

    // Disable Digital input on PF0-2 (power save)
    DIDR0 = (7<<ADC0D);

    // mt PORTB = (15<<PORTB0);       // Enable pullup on 
    PORTB = (15<<PB0);       // Enable pullup on 
    // mt PORTE = (15<<PORTE4);
    PORTE = (15<<PE4);

    sbiBF(DDRB, 5);               // set OC1A as output
    sbiBF(PORTB, 5);              // set OC1A high
            
    Button_Init();              // Initialize pin change interrupt on joystick
    
    RTC_init();                 // Start timer2 asynchronous, used for RTC clock


    USART_Init(12);             // Baud rate = 9600bps

    
    DF_SPI_init();              // init the SPI interface to communicate with the DataFlash
    
    tst = Read_DF_status();

    DF_CS_inactive;             // disable DataFlash
        
    LCD_Init();                 // initialize the LCD
}


/*****************************************************************************
*
*   Function name : PowerSaveFunc
*
*   Returns :       char ST_state (to the state-machine)
*
*   Parameters :    char input (from joystick)
*
*   Purpose :       Enable power save
*
*****************************************************************************/
// mt __flash char TEXT_POWER[]                     = "Press enter to sleep";
// mt jw const char TEXT_POWER[]  PROGMEM  = "Press enter to sleep";

void PowerSaveFunc(char input)
{
    static char enter = 1;    
    
    if(enter)
    {
        enter = 0;
        //mt jw LCD_puts_f(TEXT_POWER, 1);
        LCD_puts_f(PSTR("Press enter to sleep"), 1);
    }
    else if(input == KEY_ENTER)
    {
        PowerSave = TRUE;
        enter = 1;
//        return ST_AVRBF;
    }
    else if (input == KEY_PREV)
    {
        enter = 1;
//        return ST_OPTIONS_POWER_SAVE;
    }
        
//    return ST_OPTIONS_POWER_SAVE_FUNC;

}




/*****************************************************************************
*
*   Function name : AutoPower
*
*   Returns :       char ST_state (to the state-machine)
*
*   Parameters :    char input (from joystick)
*
*   Purpose :       Enable/Disable auto power save
*
*****************************************************************************/

static void AutoPowerShowMin(void)
{
	char PH,PL; 
	
	PH = CHAR2BCD2(PowerSaveTimeout);
	PL = (PH & 0x0F) + '0';
	PH = (PH >> 4) + '0';
	
	LCD_putc(0, 'M');
	LCD_putc(1, 'I');
	LCD_putc(2, 'N');
	LCD_putc(3, ' ');
	LCD_putc(4, PH);
	LCD_putc(5, PL);
	LCD_putc(6, '\0');
	
	LCD_UpdateRequired(TRUE, 0);
}

void AutoPower(char input)
{
    static char enter = 1;    
    
    if(enter)
    {
        enter = 0;
        
        if(AutoPowerSave)  
            AutoPowerShowMin();
        else
            LCD_puts_f(PSTR("Off"),1);	// mt LCD_puts("Off", 1);        
    }
    else if(input == KEY_ENTER)
    {
         enter = 1;

//         return ST_OPTIONS_AUTO_POWER_SAVE;
    }
    else if (input == KEY_PLUS)
    {

        PowerSaveTimeout += 5;
         
        if(PowerSaveTimeout > 90)
        {
            PowerSaveTimeout = 90;
        }
        else
        {    
            AutoPowerSave = TRUE;
            AutoPowerShowMin();
        }
    }
    else if (input == KEY_MINUS)
    {
        if(PowerSaveTimeout)
            PowerSaveTimeout -= 5;

        if(PowerSaveTimeout < 5)
        {
            AutoPowerSave = FALSE;
            PowerSaveTimeout = 0;
            LCD_puts_f(PSTR("Off"),1);	// mt LCD_puts("Off", 1);
        }
        else
        {   
            AutoPowerSave = TRUE;
            AutoPowerShowMin();
        }
    }
        
//    return ST_OPTIONS_AUTO_POWER_SAVE_FUNC;    
}



/*****************************************************************************
*
*   Function name : Delay
*
*   Returns :       None
*
*   Parameters :    unsigned int millisec
*
*   Purpose :       Delay-loop
*
*****************************************************************************/
void Delay(unsigned int millisec)
{
	// mt, int i did not work in the simulator:  int i; 
	uint8_t i;

	while (millisec--) {
		for (i=0; i<125; i++) {
			asm volatile ("nop"::);
		}
	}
}


/*****************************************************************************
*
*   Function name : OSCCAL_calibration
*
*   Returns :       None
*
*   Parameters :    None
*
*   Purpose :       Calibrate the internal OSCCAL byte, using the external 
*                   32,768 kHz crystal as reference
*
*****************************************************************************/
void OSCCAL_calibration(void)
{
    unsigned char calibrate = FALSE;
    int temp;
    unsigned char tempL;

    CLKPR = (1<<CLKPCE);        // set Clock Prescaler Change Enable
    // set prescaler = 8, Inter RC 8Mhz / 8 = 1Mhz
    CLKPR = (1<<CLKPS1) | (1<<CLKPS0);
    
    TIMSK2 = 0;             //disable OCIE2A and TOIE2

    ASSR = (1<<AS2);        //select asynchronous operation of timer2 (32,768kHz)
    
    OCR2A = 200;            // set timer2 compare value 

    TIMSK0 = 0;             // delete any interrupt sources
        
    TCCR1B = (1<<CS10);     // start timer1 with no prescaling
    TCCR2A = (1<<CS20);     // start timer2 with no prescaling

    while((ASSR & 0x01) | (ASSR & 0x04));       //wait for TCN2UB and TCR2UB to be cleared

    Delay(1000);    // wait for external crystal to stabilise
    
    while(!calibrate)
    {
        cli(); // mt __disable_interrupt();  // disable global interrupt
        
        TIFR1 = 0xFF;   // delete TIFR1 flags
        TIFR2 = 0xFF;   // delete TIFR2 flags
        
        TCNT1H = 0;     // clear timer1 counter
        TCNT1L = 0;
        TCNT2 = 0;      // clear timer2 counter
           
        // shc/mt while ( !(TIFR2 && (1<<OCF2A)) );   // wait for timer2 compareflag    
        while ( !(TIFR2 & (1<<OCF2A)) );   // wait for timer2 compareflag

        TCCR1B = 0; // stop timer1

        sei(); // __enable_interrupt();  // enable global interrupt
    
        // shc/mt if ( (TIFR1 && (1<<TOV1)) )
        if ( (TIFR1 & (1<<TOV1)) )
        {
            temp = 0xFFFF;      // if timer1 overflows, set the temp to 0xFFFF
        }
        else
        {   // read out the timer1 counter value
            tempL = TCNT1L;
            temp = TCNT1H;
            temp = (temp << 8);
            temp += tempL;
        }
    
        if (temp > 6250)
        {
            OSCCAL--;   // the internRC oscillator runs to fast, decrease the OSCCAL
        }
        else if (temp < 6120)
        {
            OSCCAL++;   // the internRC oscillator runs to slow, increase the OSCCAL
        }
        else
            calibrate = TRUE;   // the interRC is correct

        TCCR1B = (1<<CS10); // start timer1
    }
}


